package com.wlyuan.open.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.wlyuan.area.api.AreaParseService;
import com.wlyuan.area.constants.AnalyzerStatusEnum;
import com.wlyuan.area.constants.LocationTypeEnum;
import com.wlyuan.area.dto.parse.AreaParseDTO;
import com.wlyuan.basic.api.*;
import com.wlyuan.basic.dto.customer.CustomerDetailDTO;
import com.wlyuan.basic.dto.goods.GoodsBatchCreationDTO;
import com.wlyuan.basic.dto.goods.GoodsDetailDTO;
import com.wlyuan.basic.dto.goods.GoodsTypeDetailDTO;
import com.wlyuan.core.exception.AssertUtils;
import com.wlyuan.core.exception.ValueError;
import com.wlyuan.core.marker.NamedStatus;
import com.wlyuan.core.named.TrackType;
import com.wlyuan.ere.api.TmsDevicesStandService;
import com.wlyuan.ere.common.execption.DeviceNotExistException;
import com.wlyuan.ere.common.execption.OrderNotExitException;
import com.wlyuan.open.annotation.OpenApi;
import com.wlyuan.open.assembler.OrderAssembler;
import com.wlyuan.open.dto.order.OrderReceiptDelayReasonDTO;
import com.wlyuan.open.dto.order.OrderSignDelayReasonDTO;
import com.wlyuan.open.exception.AreaMatchException;
import com.wlyuan.open.exception.CarrierForbiddenException;
import com.wlyuan.open.exception.CarrierNoFoundException;
import com.wlyuan.open.exception.CustomerNoFoundException;
import com.wlyuan.open.order.OrderDefinitions;
import com.wlyuan.open.order.dto.*;
import com.wlyuan.open.order.vo.OpenOrderDetailVO;
import com.wlyuan.open.translator.OrderTranslator;
import com.wlyuan.order.api.IOrderDraftService;
import com.wlyuan.order.api.IOrderService;
import com.wlyuan.order.dto.OrderCreationShouldDTO;
import com.wlyuan.order.dto.OrderFilterDTO;
import com.wlyuan.order.dto.draft.OrderDraftGoodsDTO;
import com.wlyuan.order.dto.rewrite.OrderRewriteDTO;
import com.wlyuan.order.dto.track.OrderTrackDTO;
import com.wlyuan.tools.Page;
import com.wlyuan.tools.Sort;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author lijing
 */
@Api(tags = "订单")
@Validated
@RequestMapping("/api/order")
@RestController
@Slf4j
public class OpenOrderController {

    @DubboReference(check = false, retries = -1)
    private IOrderService orderService;
    @DubboReference(check = false, retries = -1)
    private IOrderDraftService orderDraftService;
    @DubboReference(check = false)
    private ICustomerService customerService;
    @DubboReference(check = false)
    private IConsigneeService consigneeService;
    @DubboReference(check = false)
    private AreaParseService areaParseService;
    @DubboReference(check = false)
    private IGoodsService goodsService;
    @DubboReference(check = false)
    private ICarrierService carrierService;
    @DubboReference(check = false)
    private IGoodsTypeService goodsTypeService;
    @DubboReference(check = false)
    private TmsDevicesStandService tmsDevicesStandService;

    @ApiOperation("查询订单信息")
    @GetMapping("/{orderId}")
    public OpenOrderDetailVO getByOrderId(@PathVariable Long orderId) {
        var orderDetail = orderService.getOrderById(orderId);
        logger.info("查询订单信息orderDetail:{}", JSONObject.toJSONString(orderDetail));
        return OrderTranslator.translate(orderDetail);
    }

    // todo 应该有自己的DTO
    @ApiOperation("根据单据编号查询订单详情")
    @PostMapping("/orderNo")
    public OpenOrderDetailVO getByOrderNo(@RequestBody OpenOrderExistsDTO openOrderExistsDTO) {
        var orderDetail = orderService.getOrderByNo(openOrderExistsDTO.getContractNo(), openOrderExistsDTO.getTenantId());
        AssertUtils.assertTrue(orderDetail != null, new OrderNotExitException("订单不存在"));
        logger.info("查询订单信息orderDetail:{}", JSONObject.toJSONString(orderDetail));
        return OrderTranslator.translate(orderDetail);
    }

    @ApiOperation("创建订单")
    @PostMapping("/")
    @OpenApi
    public OpenOrderDetailVO createOrder(@RequestBody @Valid OpenOrderCreationDTO creation) {
        logger.info("createOrder-openDTO:{}", JSONObject.toJSONString(creation));
        //校验必填值
        checkRequireParam(creation);
        var order = createOrderCreationDTO(creation);
        logger.info("order:{}", JSONObject.toJSONString(order));
        var orderDetail = orderService.create(order);
        logger.info("创建订单orderDetail:{}", JSONObject.toJSONString(orderDetail));
        return OrderTranslator.translate(orderDetail);
    }

    @ApiOperation("创建订单-草稿")
    @PostMapping("/draft")
    public OpenOrderDetailVO createOrderDraft(@RequestBody @Valid OpenOrderCreationDTO creation) {
        logger.info("draft-openDTO:{}", creation);
        matchArea(creation.getToAddress(), creation.getTenantId());
        matchArea(creation.getFromAddress(), creation.getTenantId());
        var draft = OrderAssembler.assembleDraft(creation);
        //如果有有发货方，则设置发货方id
        if (StringUtils.isNotBlank(creation.getCustomerName())) {
            var customer = customerService.getCustomerByName(creation.getCustomerName(), creation.getTenantId());
            if (customer != null) {
                draft.setCustomerId(customer.getCustomerId());
            }
        }
        //如果有收货方，则设置收货方id
        if (StringUtils.isNotBlank(creation.getConsigneeName()) && draft.getCustomerId() != null) {
            var consignee = consigneeService.getConsigneeByName(creation.getConsigneeName(),
                    draft.getCustomerId(), draft.getTenantId());
            if (consignee != null) {
                draft.setConsigneeId(consignee.getConsigneeId());
            }
        }
        // 如果有物品，则查询物品类型id，物品类型id为空，则只返回物品类型名称
        if (CollectionUtil.isNotEmpty(draft.getGoodsList())) {
            var typeNames = draft.getGoodsList().stream().
                    map(OrderDraftGoodsDTO::getType).collect(Collectors.toList());
            Map<String, GoodsTypeDetailDTO> typeMap = null;
            if (CollectionUtil.isNotEmpty(typeNames)) {
                var goodsTypes = goodsTypeService.getTypeByNames(typeNames, draft.getTenantId());
                if (CollectionUtil.isNotEmpty(goodsTypes)) {
                    typeMap = goodsTypes.stream().collect(
                            Collectors.toMap(GoodsTypeDetailDTO::getName, Function.identity(), (v1, v2) -> v2));
                }
            }
            for (OrderDraftGoodsDTO draftGoods : draft.getGoodsList()) {
                if (typeMap != null && typeMap.get(draftGoods.getType()) != null) {
                    draftGoods.setGoodsTypeId(typeMap.get(draftGoods.getType()).getGoodsTypeId());
                } else {
                    draftGoods.setGoodsTypeId(null);
                    draftGoods.setType(draftGoods.getType());
                }
            }

        }
        logger.info("draft-orderDTO:{}", JSONObject.toJSONString(draft));
        var orderDetail = orderDraftService.create(draft);
        logger.info("draft-orderDetail:{}", JSONObject.toJSONString(orderDetail));
        return OrderTranslator.translate(orderDetail);
    }

    @ApiOperation(value = "检查合同号是否存在", notes = "true 存在")
    @PostMapping("/exists")
    public boolean checkContractNo(@RequestBody @Valid OpenOrderExistsDTO existsDTO) {
        return !orderService.checkContractNo(existsDTO.getContractNo(), existsDTO.getTenantId());
    }

    /**
     * todo 查询合同号分页
     *
     * @param existsDTO
     * @return
     */
    @ApiOperation(value = "根据合同号精准查询")
    @PostMapping("/search")
    @OpenApi
    public Page<OpenOrderDetailVO> search(@RequestBody OpenOrderExistsDTO existsDTO) {
        OrderFilterDTO filterDTO = new OrderFilterDTO();
        filterDTO.setContractNos(Arrays.asList(existsDTO.getContractNo()));
        filterDTO.setPage(1L);
        filterDTO.setSize(100L);
        filterDTO.setTenantId(existsDTO.getTenantId());
        var result = orderService.search(filterDTO);
        var page = new Page<OpenOrderDetailVO>(filterDTO.getPage(), filterDTO.getSize());
        page.setRecords(OrderTranslator.translate(result.getRecords()));
        page.setTotal(result.getTotal());
        return page;
    }

    @ApiOperation("关单")
    @PutMapping("/close")
    public void closeOrder(@RequestBody @Valid OpenOrderCloseDTO openClose) {
        var orderClose = OrderAssembler.assemble(openClose);
        orderService.close(orderClose);
    }

    @ApiOperation(value = "绑定设备", notes = "订单绑定设备")
    @PostMapping("/track")
    @OpenApi
    public void orderTrack(@RequestBody @Valid OpenOrderDeviceBindDTO openOrderDeviceBindDTO) {
        //根据合同号和租户id,未签收状态精准查询订单数据
        OrderFilterDTO filterDTO = new OrderFilterDTO();
        filterDTO.setStatus(20);
        filterDTO.setTenantId(openOrderDeviceBindDTO.getTenantId());
        filterDTO.setPage(1L);
        filterDTO.setSize(1L);
        List<Sort> sortList = new ArrayList<>(1);
        Sort createAtSort = new Sort();
        createAtSort.setName("createAt");
        createAtSort.setAsc(false);
        sortList.add(createAtSort);
        filterDTO.setSort(sortList);
        filterDTO.setContractNos(Arrays.asList(openOrderDeviceBindDTO.getContractNo()));
        var result = orderService.search(filterDTO);
        AssertUtils.assertTrue(result != null && CollectionUtil.isNotEmpty(result.getRecords()), new OrderNotExitException("订单合同号不正确或订单状态不可绑定设备"));
        //由于订单现在暂时未对设备所属公司做判断，所以此处先增加判断，后续订单写了，此处则删除。
        if (openOrderDeviceBindDTO.getTrackType().equals(TrackType.DEVICE.getValue())) {
            boolean isOwnDevice = tmsDevicesStandService.deviceOfCompany(openOrderDeviceBindDTO.getTrackNo(), openOrderDeviceBindDTO.getTenantId());
            AssertUtils.assertTrue(isOwnDevice, new DeviceNotExistException("设备不存在"));
        }
        var order = result.getRecords().get(0);
        OrderTrackDTO trackDTO = new OrderTrackDTO();
        trackDTO.setOrderId(order.getOrderId());
        trackDTO.setTrackNo(openOrderDeviceBindDTO.getTrackNo());
        trackDTO.setTrackType(openOrderDeviceBindDTO.getTrackType());
        trackDTO.setTenantId(openOrderDeviceBindDTO.getTenantId());
        trackDTO.setUserId(openOrderDeviceBindDTO.getUserId());
        orderService.track(trackDTO);
    }

    public OpenOrderDetailVO rewriteOrder(@RequestBody @Valid OpenOrderRewriteDTO openOrderRewriteDTO) {
        //判断orderId是否有值，如果没有值，则走创建订单的接口
        if (openOrderRewriteDTO.getOrderId() != null) {
            var sourceOrderDetail = orderService.getOrderById(openOrderRewriteDTO.getOrderId());
            AssertUtils.assertNotNull(sourceOrderDetail, new OrderNotExitException("订单不存在"));

            OrderRewriteDTO orderRewrite = new OrderRewriteDTO();
            // todo 物品信息未获取到
            OrderAssembler.merge(orderRewrite, sourceOrderDetail);
            //sourceId 取orderId
            orderRewrite.setSourceId(openOrderRewriteDTO.getOrderId().toString());
            var orderCreation = createOrderCreationDTO(openOrderRewriteDTO);
            BeanUtils.copyProperties(orderCreation, orderRewrite);
            logger.info("rewriteOrder:{}", JSONObject.toJSONString(orderRewrite));
            var orderDetail = orderService.rewrite(orderRewrite);
            logger.info("rewrite-orderDetail:{}", JSONObject.toJSONString(orderDetail));
            return OrderTranslator.translate(orderDetail);
        } else {
            return createOrder(openOrderRewriteDTO);
        }
    }

    private Integer matchArea(OrderDefinitions.Address address, Long tenantId) {
        if (address != null && StringUtils.isNotBlank(address.getProvince())
                && StringUtils.isNotBlank(address.getCity()) &&
                StringUtils.isNotBlank(address.getDistrict())) {
            Integer locationType = LocationTypeEnum.INACCURATE.getValue();
            AreaParseDTO areaParseDTO = new AreaParseDTO();
            areaParseDTO.setProvinceName(address.getProvince());
            areaParseDTO.setCityName(address.getCity());
            areaParseDTO.setDistrictName(address.getDistrict());
            areaParseDTO.setAddress(address.getAddress());
            areaParseDTO.setCheck(true);
            areaParseDTO.setTenantId(tenantId);
            var result = areaParseService.parse(areaParseDTO);
            if (!result.getStatus().equals(AnalyzerStatusEnum.SUCCESS)) {
                throw new AreaMatchException(result.getMsg());
            }
            address.setProvince(result.getArea().getProvinceName());
            address.setProvinceCode(result.getArea().getProvinceCode());
            address.setCityCode(result.getArea().getCityCode());
            address.setCity(result.getArea().getCityName());
            address.setDistrictCode(result.getArea().getDistrictCode());
            address.setDistrict(result.getArea().getDistrictName());
            if (StringUtils.isBlank(address.getLat()) || StringUtils.isBlank(address.getLon())) {
                address.setLat(result.getArea().getLatitude());
                address.setLon(result.getArea().getLongitude());
                locationType = result.getArea().getLocationType();
            } else {
                locationType = LocationTypeEnum.PRECISE.getValue();
            }
            return locationType;
        }
        return null;
    }

    private Map<String, Long> saveGoods(Long customerId, OpenOrderCreationDTO creation) {
        if (CollectionUtil.isNotEmpty(creation.getSpecs())) {
            GoodsBatchCreationDTO batchCreation = new GoodsBatchCreationDTO();
            batchCreation.setUserId(creation.getUserId());
            batchCreation.setTenantId(creation.getTenantId());
            batchCreation.setSpecs(OrderAssembler.assemble(customerId, creation.getSpecs()));
            logger.info("goods-batchCreation:{}", JSONObject.toJSONString(batchCreation));
            var goodsList = goodsService.create(batchCreation);

            var goodsMap = goodsList.stream().collect(Collectors.toMap(goods -> OrderAssembler.assembleGoodsStamp(goods.getName(),
                            goods.getType(), goods.getUnit(), goods.getSpec(), goods.getValue(), goods.getVolume(), goods.getNetWeight(), goods.getBarcode())
                    , GoodsDetailDTO::getGoodsId));
            logger.info("orderContractNo:{},goodsMap:{}", creation.getContractNo(), JSONObject.toJSONString(goodsMap));
            return goodsMap;
        }
        return null;
    }


    private void checkRequireParam(OpenOrderCreationDTO creation) {
        AssertUtils.assertNotEmpty(creation.getConsigneeName(), new ValueError("收货方名称不能为空"));
        AssertUtils.assertNotEmpty(creation.getCustomerName(), new ValueError("发货方名称不能为空"));
        AssertUtils.assertNotEmpty(creation.getContractNo(), new ValueError("合同编号不能为空"));
        AssertUtils.assertNotNull(creation.getFromContact(), new ValueError("发货联系信息不能为空"));
        AssertUtils.assertNotNull(creation.getToContact(), new ValueError("收货联系信息不能为空"));
        AssertUtils.assertNotNull(creation.getFromAddress(), new ValueError("发货地址信息不能为空"));
        AssertUtils.assertNotNull(creation.getToAddress(), new ValueError("收货地址信息不能为空"));
        AssertUtils.assertNotNull(creation.getSpecs(), new ValueError("物品信息不能为空"));
        AssertUtils.assertNotNull(creation.getFromTime(), new ValueError("发货时间不能为空"));
    }

    private OrderCreationShouldDTO createOrderCreationDTO(OpenOrderCreationDTO creation) {
        // 校验省市区
        Integer fromLocationType = matchArea(creation.getFromAddress(), creation.getTenantId());
        Integer toLocationType = matchArea(creation.getToAddress(), creation.getTenantId());
        //校验发货方
        Long tenantId = creation.getTenantId();
        CustomerDetailDTO customer = null;
        if (StringUtils.isNotBlank(creation.getCustomerName())) {
            customer = customerService.getCustomerByName(creation.getCustomerName(), tenantId);
            AssertUtils.assertNotNull(customer, new CustomerNoFoundException("发货方未找到"));
        }
        //判断承运商信息
        Long carrierId = null;
        if (StringUtils.isNotBlank(creation.getCarrierName())) {
            var carrier = carrierService.getCarrierByName(creation.getCarrierName(), creation.getTenantId());
            AssertUtils.assertNotNull(carrier, new CarrierNoFoundException("承运方未找到"));
            AssertUtils.assertTrue(carrier.getStatus().equals(NamedStatus.ENABLED.getValue())
                    || carrier.getStatus().equals(NamedStatus.AGREED.getValue()), new CarrierForbiddenException("承运方状态不正确"));
            carrierId = carrier.getCarrierId();
        }
//        //校验收货方
//        Long consigneeId = null;
//        if (StringUtils.isNotBlank(creation.getConsigneeName()) && customer != null) {
//            var consignee = consigneeService.getConsigneeByName(creation.getConsigneeName(), customer.getCustomerId(), creation.getTenantId());
//            if (consignee == null) {
//                ConsigneeCreationDTO creationDTO = new ConsigneeCreationDTO();
//                creationDTO.setCustomerId(customer.getCustomerId());
//                creationDTO.setTenantId(creation.getTenantId());
//                creationDTO.setUserId(creation.getUserId());
//                creationDTO.setName(creation.getConsigneeName());
//                creationDTO.setContactName(creation.getToContact().getName());
//                creationDTO.setContactMobile(creation.getToContact().getMobile());
//                consignee = consigneeService.create(creationDTO);
//
//            }
//            consigneeId = consignee.getConsigneeId();
//        }
        Map<String, Long> goodsMap = null;
        //新增物品或查询物品信息
        if (customer != null) {
            goodsMap = saveGoods(customer.getCustomerId(), creation);
        }
        var order = OrderAssembler.assemble(creation, goodsMap);
//        order.setCustomerId(customer.getCustomerId());
        order.setCarrierId(carrierId);
//        order.setConsigneeId(consigneeId);
        order.getFromAddress().setPrecision(fromLocationType);
        order.getToAddress().setPrecision(toLocationType);
        return order;
    }

    @ApiOperation(value = "订单签收延期原因", notes = "订单签收延期原因")
    @PostMapping("/sign/delay")
    public void orderSignDelayMark(@RequestBody OrderSignDelayReasonDTO mark) {
        orderService.mark(OrderAssembler.assemble(mark));
    }

    @ApiOperation(value = "订单回单延期原因", notes = "订单回单延期原因")
    @PostMapping("/receipt/delay")
    public void orderReceiptDelayMark(@RequestBody OrderReceiptDelayReasonDTO mark) {
        orderService.mark(OrderAssembler.assemble(mark));
    }

}
